home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Freeware 2001 May
/
SGI Freeware 2001 May - Disc 3.iso
/
dist
/
fw_elisp-intro.idb
/
usr
/
freeware
/
info
/
emacs-lisp-intro.info-10.z
/
emacs-lisp-intro.info-10
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
GNU Info File
|
1998-10-28
|
47.3 KB
|
1,164 lines
This is Info file emacs-lisp-intro.info, produced by Makeinfo version
1.67 from the input file emacs-lisp-intro.texi.
This is an introduction to `Programming in Emacs Lisp', for people
who are not programmers.
Edition 1.05, 21 October 1997
Copyright (C) 1990, '91, '92, '93, '94, '95, '97 Free Software
Foundation, Inc.
Permission is granted to make and distribute verbatim copies of this
manual provided the copyright notice and this permission notice are
preserved on all copies.
Permission is granted to copy and distribute modified versions of
this manual under the conditions for verbatim copying, provided also
that the sections entitled "Copying" and "GNU General Public License"
are included exactly as in the original, and provided that the entire
resulting derived work is distributed under the terms of a permission
notice identical to this one.
Permission is granted to copy and distribute translations of this
manual into another language, under the above conditions for modified
versions, except that this permission notice may be stated in a
translation approved by the Free Software Foundation.
File: emacs-lisp-intro.info, Node: Several files recursively, Next: Prepare the data, Prev: Several files, Up: Words in a defun
Recursively Count Words in Different Files
==========================================
Besides a `while' loop, you can work on each of a list of files with
recursion. A recursive version of `lengths-list-many-files' is short
and simple.
The recursive function has the usual parts: the `do-again-test', the
`next-step-expression', and the recursive call. The `do-again-test'
determines whether the function should call itself again, which it will
do if the `list-of-files' contains any remaining elements; the
`next-step-expression' resets the `list-of-files' to the CDR of itself,
so eventually the list will be empty; and the recursive call calls
itself on the shorter list. The complete function is shorter than this
description!
(defun recursive-lengths-list-many-files (list-of-files)
"Return list of lengths of each defun in LIST-OF-FILES."
(if list-of-files ; do-again-test
(append
(lengths-list-file
(expand-file-name (car list-of-files)))
(recursive-lengths-list-many-files
(cdr list-of-files)))))
In a sentence, the function returns the lengths' list for the first of
the `list-of-files' appended to the result of calling itself on the
rest of the `list-of-files'.
Here is a test of `recursive-lengths-list-many-files', along with
the results of running `lengths-list-file' on each of the files
individually.
Install `recursive-lengths-list-many-files' and `lengths-list-file',
if necessary, and then evaluate the following expressions. You may
need to change the files' pathnames; those here work when this Info
file and the Emacs sources are located in their customary places. To
change the expressions, copy them to the `*scratch*' buffer, edit them,
and then evaluate them.
The results are shown after the `=>'. (These results are for files
from Emacs Version 18.57; files from other versions of Emacs may
produce different results.)
(lengths-list-file
"../lisp/macros.el")
=> (176 154 86)
(lengths-list-file
"../lisp/mailalias.el")
=> (116 122 265)
(lengths-list-file
"../lisp/makesum.el")
=> (85 179)
(recursive-lengths-list-many-files
'("../lisp/macros.el"
"../lisp/mailalias.el"
"../lisp/makesum.el"))
=> (176 154 86 116 122 265 85 179)
The `recursive-lengths-list-many-files' function produces the output
we want.
The next step is to prepare the data in the list for display in a
graph.
File: emacs-lisp-intro.info, Node: Prepare the data, Prev: Several files recursively, Up: Words in a defun
Prepare the Data for Display in a Graph
=======================================
The `recursive-lengths-list-many-files' function returns a list of
numbers. Each number records the length of a function definition.
What we need to do now is transform this data into a list of numbers
suitable for generating a graph. The new list will tell how many
functions definitions contain less than 10 words and symbols, how many
contain between 10 and 19 words and symbols, how many contain between
20 and 29 words and symbols, and so on.
In brief, we need to go through the lengths' list produced by the
`recursive-lengths-list-many-files' function and count the number of
defuns within each range of lengths, and produce a list of those
numbers.
Based on what we have done before, we can readily foresee that it
should not be too hard to write a function that `CDRs' down the
lengths' list, looks at each element, determines which length range it
is in, and increments a counter for that range.
However, before beginning to write such a function, we should
consider the advantages of sorting the lengths' list first, so the
numbers are ordered from smallest to largest. First, sorting will make
it easier to count the numbers in each range, since two adjacent
numbers will either be in the same length range or in adjacent ranges.
Second, by inspecting a sorted list, we can discover the highest and
lowest number, and thereby determine the largest and smallest length
range that we will need.
* Menu:
* Sorting:: Sorting lists.
* Files List:: Making a list of files.
File: emacs-lisp-intro.info, Node: Sorting, Next: Files List, Prev: Prepare the data, Up: Prepare the data
Sorting Lists
-------------
Emacs contains a function to sort lists, called (as you might guess)
`sort'. The `sort' function takes two arguments, the list to be
sorted, and a predicate that determines whether the first of two list
elements is "less" than the second.
As we saw earlier (*note Using the Wrong Type Object as an Argument:
Wrong Type of Argument.), a predicate is a function that determines
whether some property is true or false. The `sort' function will
reorder a list according to whatever property the predicate uses; this
means that `sort' can be used to sort non-numeric lists by non-numeric
criteria--it can, for example, alphabetize a list.
The `<' function is used when sorting a numeric list. For example,
(sort '(4 8 21 17 33 7 21 7) '<)
produces this:
(4 7 7 8 17 21 21 33)
(Note that in this example, both the arguments are quoted so that the
symbols are not evaluated before being passed to `sort' as arguments.)
Sorting the list returned by the `recursive-lengths-list-many-files'
function is straightforward:
(sort
(recursive-lengths-list-many-files
'("../lisp/macros.el"
"../lisp/mailalias.el"
"../lisp/makesum.el"))
'<)
which produces:
(85 86 116 122 154 176 179 265)
(Note that in this example, the first argument to `sort' is not quoted,
since the expression must be evaluated so as to produce the list that
is passed to `sort'.)
File: emacs-lisp-intro.info, Node: Files List, Prev: Sorting, Up: Prepare the data
Making a List of Files
----------------------
The `recursive-lengths-list-many-files' function requires a list of
files as its argument. For our test examples, we constructed such a
list by hand; but the Emacs Lisp source directory is too large for us
to do for that. Instead, we need to use the `directory-files' function
to construct a list for us.
The `directory-files' function takes three arguments: the first
argument is the name of a directory, a string; a non-`nil' second
argument causes the function to return the files' absolute pathnames;
and the third argument is a selector. If it contains a regular
expression (rather than `nil'), only pathnames that match that regular
expression are returned.
Thus, on my system,
(length
(directory-files "../lisp" t "\\.el$"))
tells me that my version 19.25 Lisp sources directory contains 307
`.el' files.
An expression to sort the list returned by
`recursive-lengths-list-many-files' looks like this:
(sort
(recursive-lengths-list-many-files
(directory-files "../lisp" t "\\.el$"))
'<)
Our immediate goal is to generate a list that tells us how many
function definitions contain fewer than 10 words and symbols, how many
contain between 10 and 19 words and symbols, how many contain between
20 and 29 words and symbols, and so on. With a sorted list of numbers,
this is easy: count how many elements of the list are smaller than 10,
then, after moving past the numbers just counted, count how many are
smaller than 20, then, after moving past the numbers just counted,
count how many are smaller than 30, and so on. Each of the numbers,
10, 20, 30, 40, and the like, is one larger than the top of that range.
We can call the list of such numbers the `top-of-ranges' list.
If we wanted to, we could generate this list automatically, but it is
simpler to write a list manually. Here it is:
(defvar top-of-ranges
'(10 20 30 40 50
60 70 80 90 100
110 120 130 140 150
160 170 180 190 200
210 220 230 240 250
260 270 280 290 300)
"List specifying ranges for `defuns-per-range'.")
To change the ranges, we edit this list.
Next, we need to write the function that creates the list of the
number of definitions within each range. Clearly, this function must
take the `sorted-lengths' and the `top-of-ranges' lists as arguments.
The `defuns-per-range' function must do two things again and again:
it must count the number of definitions within a range specified by the
current top-of-range value; and it must shift to the next higher value
in the `top-of-ranges' list after counting the number of definitions in
the current range. Since each of these actions is repetitive, we can
use `while' loops for the job. One loop counts the number of
definitions in the range defined by the current top-of-range value, and
the other loop selects each of the top-of-range values in turn.
Several entries of the `sorted-lengths' list are counted for each
range; this means that the loop for the `sorted-lengths' list will be
inside the loop for the `top-of-ranges' list, like a small gear inside
a big gear.
The inner loop counts the number of definitions within the range. It
is a simple counting loop of the type we have seen before. (*Note A
loop with an incrementing counter: Incrementing Loop.) The
true-or-false test of the loop tests whether the value from the
`sorted-lengths' list is smaller than the current value of the top of
the range. If it is, the function increments the counter and tests the
next value from the `sorted-lengths' list.
The inner loop looks like this:
(while LENGTH-ELEMENT-SMALLER-THAN-TOP-OF-RANGE
(setq number-within-range (1+ number-within-range))
(setq sorted-lengths (cdr sorted-lengths)))
The outer loop must start with the lowest value of the
`top-of-ranges' list, and then be set to each of the succeeding higher
values in turn. This can be done with a loop like this:
(while top-of-ranges
BODY-OF-LOOP...
(setq top-of-ranges (cdr top-of-ranges)))
Put together, the two loops look like this:
(while top-of-ranges
;; Count the number of elements within the current range.
(while LENGTH-ELEMENT-SMALLER-THAN-TOP-OF-RANGE
(setq number-within-range (1+ number-within-range))
(setq sorted-lengths (cdr sorted-lengths)))
;; Move to next range.
(setq top-of-ranges (cdr top-of-ranges)))
In addition, in each circuit of the outer loop, Emacs should record
the number of definitions within that range (the value of
`number-within-range') in a list. We can use `cons' for this purpose.
(*Note `cons': cons.)
The `cons' function works fine, except that the list it constructs
will contain the number of definitions for the highest range at its
beginning and the number of definitions for the lowest range at its
end. This is because `cons' attaches new elements of the list to the
beginning of the list, and since the two loops are working their way
through the lengths' list from the lower end first, the
`defuns-per-range-list' will end up largest number first. But we will
want to print our graph with smallest values first and the larger
later. The solution is to reverse the order of the
`defuns-per-range-list'. We can do this using the `nreverse' function,
which reverses the order of a list.
For example,
(nreverse '(1 2 3 4))
produces:
(4 3 2 1)
Note that the `nreverse' function is "destructive"--that is, it
changes the list to which it is applied; this contrasts with the `car'
and `cdr' functions, which are non-destructive. In this case, we do
not want the original `defuns-per-range-list', so it does not matter
that it is destroyed. (The `reverse' function provides a reversed copy
of a list, leaving the original list as is.)
Put all together, the `defuns-per-range' looks like this:
(defun defuns-per-range (sorted-lengths top-of-ranges)
"SORTED-LENGTHS defuns in each TOP-OF-RANGES range."
(let ((top-of-range (car top-of-ranges))
(number-within-range 0)
defuns-per-range-list)
;; Outer loop.
(while top-of-ranges
;; Inner loop.
(while (and
;; Need number for numeric test.
(car sorted-lengths)
(< (car sorted-lengths) top-of-range))
;; Count number of definitions within current range.
(setq number-within-range (1+ number-within-range))
(setq sorted-lengths (cdr sorted-lengths)))
;; Exit inner loop but remain within outer loop.
(setq defuns-per-range-list
(cons number-within-range defuns-per-range-list))
(setq number-within-range 0) ; Reset count to zero.
;; Move to next range.
(setq top-of-ranges (cdr top-of-ranges))
;; Specify next top of range value.
(setq top-of-range (car top-of-ranges)))
;; Exit outer loop and count the number of defuns larger than
;; the largest top-of-range value.
(setq defuns-per-range-list
(cons
(length sorted-lengths)
defuns-per-range-list))
;; Return a list of the number of definitions within each range,
;; smallest to largest.
(nreverse defuns-per-range-list)))
The function is straightforward except for one subtle feature. The
true-or-false test of the inner loop looks like this:
(and (car sorted-lengths)
(< (car sorted-lengths) top-of-range))
instead of like this:
(< (car sorted-lengths) top-of-range)
The purpose of the test is to determine whether the first item in the
`sorted-lengths' list is less than the value of the top of the range.
The simple version of the test works fine unless the
`sorted-lengths' list has a `nil' value. In that case, the `(car
sorted-lengths)' expression function returns `nil'. The `<' function
cannot compare a number to `nil', which is an empty list, so Emacs
signals an error and stops the function from attempting to continue to
execute.
The `sorted-lengths' list always becomes `nil' when the counter
reaches the end of the list. This means that any attempt to use the
`defuns-per-range' function with the simple version of the test will
fail.
We solve the problem by using the `(car sorted-lengths)' expression
in conjunction with the `and' expression. The `(car sorted-lengths)'
expression returns a non-`nil' value so long as the list has at least
one number within it, but returns `nil' if the list is empty. The
`and' expression first evaluates the `(car sorted-lengths)' expression,
and if it is `nil', returns false *without* evaluating the `<'
expression. But if the `(car sorted-lengths)' expression returns a
non-`nil' value, the `and' expression evaluates the `<' expression, and
returns that value as the value of the `and' expression.
This way, we avoid an error. *Note forward-paragraph::, for more
information about `and'.
Here is a short test of the `defuns-per-range' function. First,
evaluate the expression that binds (a shortened) `top-of-ranges' list
to the list of values, then evaluate the expression for binding the
`sorted-lengths' list, and then evaluate the `defuns-per-range'
function.
;; (Shorter list than we will use later.)
(setq top-of-ranges
'(110 120 130 140 150
160 170 180 190 200))
(setq sorted-lengths
'(85 86 110 116 122 129 154 176 179 200 265 300 300))
(defuns-per-range sorted-lengths top-of-ranges)
The list returned looks like this:
(2 2 2 0 0 1 0 2 0 0 4)
Indeed, there are two elements of the `sorted-lengths' list smaller
than 110, two elements between 110 and 119, two elements between 120
and 129, and so on. There are four elements with a value of 200 or
larger.
File: emacs-lisp-intro.info, Node: Readying a Graph, Next: Emacs Initialization, Prev: Words in a defun, Up: Top
Readying a Graph
****************
Our goal is to construct a graph showing the numbers of function
definitions of various lengths in the Emacs lisp sources.
As a practical matter, if you were creating a graph, you would
probably use a program such as `gnuplot' to do the job. (`gnuplot' is
nicely integrated into GNU Emacs.) In this case, however, we create
one from scratch, and in the process we will reaquaint ourselves with
some of what we learned before and learn more.
In this chapter, we will first write a simple graph printing
function. This first definition will be a "prototype", a rapidly
written function that enables us to reconnoiter this unknown
graph-making territory. We will discover dragons, or find that they
are myth. After scouting the terrain, we will feel more confident and
enhance the function to label the axes automatically.
* Menu:
* Columns of a graph:: How to print individual columns.
* graph-body-print:: How to print the body of a graph.
* recursive-graph-body-print::
* Printed Axes::
* Line Graph Exercise::
File: emacs-lisp-intro.info, Node: Columns of a graph, Next: graph-body-print, Prev: Readying a Graph, Up: Readying a Graph
Printing the Columns of a Graph
===============================
Since Emacs is designed to be flexible and work with all kinds of
terminals, including character-only terminals, the graph will need to
be made from one of the `typewriter' symbols. An asterisk will do; as
we enhance the graph-printing function, we can make the choice of
symbol a user option.
We can call this function `graph-body-print'; it will take a
`numbers-list' as its only argument. At this stage, we will not label
the graph, but only print its body.
The `graph-body-print' function inserts a vertical column of
asterisks for each element in the `numbers-list'. The height of each
line is determined by the value of that element of the `numbers-list'.
Inserting columns is a repetitive act; that means that this function
can be written either with a `while' loop or recursively.
Our first challenge is to discover how to print a column of
asterisks. Usually, in Emacs, we print characters onto a screen
horizontally, line by line, by typing. We have two routes we can
follow: write our own column-insertion function or discover whether one
exists in Emacs.
To see whether there is one in Emacs, we can use the `M-x apropos'
command. This command is like the `C-h a' (command-apropos) command,
except that the latter finds only those functions that are commands.
The `M-x apropos' command lists all symbols that match a regular
expression, including functions that are not interactive.
What we want to look for is some command that prints or inserts
columns. Very likely, the name of the function will contain either the
word `print' or the word `insert' or the word `column'. Therefore, we
can simply type `M-x apropos RET print\|insert\|column RET' and look at
the result. On my system, this command takes quite some time, and then
produces a list of 79 functions and variables. Scanning down the list,
the only function that looks as if it might do the job is
`insert-rectangle'. Indeed, this is the function we want; its
documentation says:
insert-rectangle:
Insert text of RECTANGLE with upper left corner at point.
RECTANGLE's first line is inserted at point,
its second line is inserted at a point vertically under point, etc.
RECTANGLE should be a list of strings.
We can run a quick test, to make sure it does what we expect of it.
Here is the result of placing the cursor after the
`insert-rectangle' expression and typing `C-u C-x C-e'
(`eval-last-sexp'). The function inserts the strings `"first"',
`"second"', and `"third"' at and below point. Also the function
returns `nil'.
(insert-rectangle '("first" "second" "third"))first
second
third
nil
Of course, we won't be inserting the text of the `insert-rectangle'
expression itself into the buffer in which we are making the graph, but
will call the function from our program. We shall, however, have to
make sure that point is in the buffer at the place where the
`insert-rectangle' function will insert its column of strings.
If you are reading this in Info, you can see how this works by
switching to another buffer, such as the `*scratch*' buffer, placing
point somewhere in the buffer, typing `M-:', typing the
`insert-rectangle' expression into the minibuffer at the prompt, and
then typing <RET>. This causes Emacs to evaluate the expression in the
minibuffer, but to use as the value of point the position of point in
the `*scratch*' buffer. (`M-:' is the keybinding for
`eval-expression'.)
We find when we do this that point ends up at the end of the last
inserted line--that is to say, this function moves point as a
side-effect. If we were to repeat the command, with point at this
position, the next insertion would be below and to the right of the
previous insertion. We don't want this! If we are going to make a bar
graph, the columns need to be beside each other.
So we discover that each cycle of the column-inserting `while' loop
must reposition point to the place we want it, and that place will be
at the top, not the bottom, of the column. Moreover, we remember that
when we print a graph, we do not expect all the columns to be the same
height. This means that the top of each column may be at a different
height from the previous one. We cannot simply reposition point to the
same line each time, but moved over to the right--or perhaps we can...
We are planning to make the columns of the bar graph out of
asterisks. The number of asterisks in the column is the number
specified by the current element of the `numbers-list'. We need to
construct a list of asterisks of the right length for each call to
`insert-rectangle'. If this list consists solely of the requisit
number of asterisks, then we will have position point the right number
of lines above the base for the graph to print correctly. This could
be difficult.
Alternatively, if we can figure out some way to pass
`insert-rectangle' a list of the same length each time, then we can
place point on the same line each time, but move it over one column to
the right for each new column. If we do this, however, some of the
entries in the list passed to `insert-rectangle' must be blanks rather
than asterisks. For example, if the maximum height of the graph is 5,
but the height of the column is 3, then `insert-rectangle' requires an
argument that looks like this:
(" " " " "*" "*" "*")
This last proposal is not so difficult, so long as we can determine
the column height. There are two ways for us to specify the column
height: we can arbitrarily state what it will be, which would work fine
for graphs of that height; or we can search through the list of numbers
and use the maximum height of the list as the maximum height of the
graph. If the latter operation were difficult, then the former
procedure would be easiest, but there is a function built into Emacs
that determines the maximum of its arguments. We can use that
function. The function is called `max' and it returns the largest of
all its arguments, which must be numbers. Thus, for example,
(max 3 4 6 5 7 3)
returns 7. (A corresponding function called `min' returns the smallest
of all its arguments.)
However, we cannot simply call `max' on the `numbers-list'; the
`max' function expects numbers as its argument, not a list of numbers.
Thus, the following expression,
(max '(3 4 6 5 7 3))
produces the following error message;
Wrong type of argument: integer-or-marker-p, (3 4 6 5 7 3)
We need a function that passes a list of arguments to a function.
This function is `apply'. This function `applies' its first argument
(a function) to its remaining arguments, the last of which may be a
list.
For example,
(apply 'max 3 4 7 3 '(4 8 5))
returns 8.
(Incidentally, I don't know how you would learn of this function
without a book such as this. It is possible to discover other
functions, like `search-forward' or `insert-rectangle', by guessing at
a part of their names and then using `apropos'. Even though its base
in metaphor is clear--`apply' its first argument to the rest--I doubt a
novice would come up with that particular word when using `apropos' or
other aid. Of course, I could be wrong; after all, the function was
first named by someone who had to invent it.)
The second and subsequent arguments to `apply' are optional, so we
can use `apply' to call a function and pass the elements of a list to
it, like this, which also returns 8:
(apply 'max '(4 8 5))
This latter way is how we will use `apply'. The
`recursive-lengths-list-many-files' function returns a numbers' list to
which we can apply `max' (we could also apply `max' to the sorted
numbers' list; it does not matter whether the list is sorted or not.)
Hence, the operation for finding the maximum height of the graph is
this:
(setq max-graph-height (apply 'max numbers-list))
Now we can return to the question of how to create a list of strings
for a column of the graph. Told the maximum height of the graph and
the number of asterisks that should appear in the column, the function
should return a list of strings for the `insert-rectangle' command to
insert.
Each column is made up of asterisks or blanks. Since the function is
passed the value of the height of the column and the number of
asterisks in the column, the number of blanks can be found by
subtracting the number of asterisks from the height of the column.
Given the number of blanks and the number of asterisks, two `while'
loops can be used to construct the list:
;;; First version.
(defun column-of-graph (max-graph-height actual-height)
"Return list of strings that is one column of a graph."
(let ((insert-list nil)
(number-of-top-blanks
(- max-graph-height actual-height)))
;; Fill in asterisks.
(while (> actual-height 0)
(setq insert-list (cons "*" insert-list))
(setq actual-height (1- actual-height)))
;; Fill in blanks.
(while (> number-of-top-blanks 0)
(setq insert-list (cons " " insert-list))
(setq number-of-top-blanks
(1- number-of-top-blanks)))
;; Return whole list.
insert-list))
If you install this function and then evaluate the following
expression you will see that it returns the list as desired:
(column-of-graph 5 3)
returns
(" " " " "*" "*" "*")
As written, `column-of-graph' contains a major flaw: the symbols
used for the blank and for the marked entries in the column are
`hard-coded' as a space and asterisk. This is fine for a prototype,
but you, or another user, may wish to use other symbols. For example,
in testing the graph function, you many want to use a period in place
of the space, to make sure the point is being repositioned properly
each time the `insert-rectangle' function is called; or you might want
to substitute a `+' sign or other symbol for the asterisk. You might
even want to make a graph-column that is more than one display column
wide. The program should be more flexible. The way to do that is to
replace the blank and the asterisk with two variables that we can call
`graph-blank' and `graph-symbol' and define those variables separately.
Also, the documentation is not well written. These considerations
lead us to the second version of the function:
(defvar graph-symbol "*"
"String used as symbol in graph, usually an asterisk.")
(defvar graph-blank " "
"String used as blank in graph, usually a blank space.
graph-blank must be the same number of columns wide
as graph-symbol.")
(For an explanation of `defvar', see *Note Initializing a Variable with
`defvar': defvar.)
;;; Second version.
(defun column-of-graph (max-graph-height actual-height)
"Return list of MAX-GRAPH-HEIGHT strings;
ACTUAL-HEIGHT are graph-symbols.
The graph-symbols are contiguous entries at the end
of the list.
The list will be inserted as one column of a graph.
The strings are either graph-blank or graph-symbol."
(let ((insert-list nil)
(number-of-top-blanks
(- max-graph-height actual-height)))
;; Fill in `graph-symbols'.
(while (> actual-height 0)
(setq insert-list (cons graph-symbol insert-list))
(setq actual-height (1- actual-height)))
;; Fill in `graph-blanks'.
(while (> number-of-top-blanks 0)
(setq insert-list (cons graph-blank insert-list))
(setq number-of-top-blanks
(1- number-of-top-blanks)))
;; Return whole list.
insert-list))
If we wished, we could rewrite `column-of-graph' a third time to
provide optionally for a line graph as well as for a bar graph. This
would not be hard to do. One way to think of a line graph is that it
is no more than a bar graph in which the part of each bar that is below
the top is blank. To construct a column for a line graph, the function
first constructs a list of blanks that is one shorter than the value,
then it uses `cons' to attach a graph symbol to the list; then it uses
`cons' again to attach the `top blanks' to the list.
It is easy to see how to write such a function, but since we don't
need it, we will not do it. But the job could be done, and if it were
done, it would be done with `column-of-graph'. Even more important, it
is worth noting that few changes would have to be made anywhere else.
The enhancement, if we ever wish to make it, is simple.
Now, finally, we come to our first actual graph printing function.
This prints the body of a graph, not the labels for the vertical and
horizontal axes, so we can call this `graph-body-print'.
File: emacs-lisp-intro.info, Node: graph-body-print, Next: recursive-graph-body-print, Prev: Columns of a graph, Up: Readying a Graph
The `graph-body-print' Function
===============================
After our preparation in the preceding section, the
`graph-body-print' function is straightforward. The function will
print column after column of asterisks and blanks, using the elements
of a numbers' list to specify the number of asterisks in each column.
This is a repetitive act, which means we can use a decrementing `while'
loop or recursive function for the job. In this section, we will write
the definition using a `while' loop.
The `column-of-graph' function requires the height of the graph as
an argument, so we should determine and record that as a local variable.
This leads us to the following template for the `while' loop version
of this function:
(defun graph-body-print (numbers-list)
"DOCUMENTATION..."
(let ((height ...
...))
(while numbers-list
INSERT-COLUMNS-AND-REPOSITION-POINT
(setq numbers-list (cdr numbers-list)))))
We need to fill in the slots of the template.
Clearly, we can use the `(apply 'max numbers-list)' expression to
determine the height of the graph.
The `while' loop will cycle through the `numbers-list' one element
at a time. As it is shortened by the `(setq numbers-list (cdr
numbers-list))' expression, the CAR of each instance of the list is the
value of the argument for `column-of-graph'.
At each cycle of the `while' loop, the `insert-rectangle' function
inserts the list returned by `column-of-graph'. Since the
`insert-rectangle' function moves point to the lower right of the
inserted rectangle, we need to save the location of point at the time
the rectangle is inserted, move back to that position after the
rectangle is inserted, and then move horizontally to the next place
from which `insert-rectangle' is called.
If the inserted columns are one character wide, as they will be if
single blanks and asterisks are used, the repositioning command is
simply `(forward-char 1)'; however, the width of a column may be
greater than one. This means that the repositioning command should be
written `(forward-char symbol-width)'. The `symbol-width' itself is
the length of a `graph-blank' and can be found using the expression
`(length graph-blank)'. The best place to bind the `symbol-width'
variable to the value of the width of graph column is in the varlist of
the `let' expression.
These considerations lead to the following function definition:
(defun graph-body-print (numbers-list)
"Print a bar graph of the NUMBERS-LIST.
The numbers-list consists of the Y-axis values."
(let ((height (apply 'max numbers-list))
(symbol-width (length graph-blank))
from-position)
(while numbers-list
(setq from-position (point))
(insert-rectangle
(column-of-graph height (car numbers-list)))
(goto-char from-position)
(forward-char symbol-width)
;; Draw graph column by column.
(sit-for 0)
(setq numbers-list (cdr numbers-list)))
;; Place point for X axis labels.
(forward-line height)
(insert "\n")
))
The one unexpected expression in this function is the `(sit-for 0)'
expression in the `while' loop. This expression makes the graph
printing operation more interesting to watch than it would be
otherwise. The expression causes Emacs to `sit' or do nothing for a
zero length of time and then redraw the screen. Placed here, it causes
Emacs to redraw the screen column by column. Without it, Emacs would
not redraw the screen until the function exits.
We can test `graph-body-print' with a short list of numbers.
1. Install `graph-symbol', `graph-blank', `column-of-graph' and
`graph-body-print'.
2. Copy the following expression:
(graph-body-print '(1 2 3 4 6 4 3 5 7 6 5 2 3))
3. Switch to the `*scratch*' buffer and place the cursor where you
want the graph to start.
4. Type `M-:' (`eval-expression').
5. Yank the `graph-body-print' expression into the minibuffer with
`C-y' (`yank)'.
6. Press <RET> to evaluate the `graph-body-print' expression.
Emacs will print a graph like this:
*
* **
* ****
*** ****
********* *
************
*************
File: emacs-lisp-intro.info, Node: recursive-graph-body-print, Next: Printed Axes, Prev: graph-body-print, Up: Readying a Graph
The `recursive-graph-body-print' Function
=========================================
The `graph-body-print' function may also be written recursively. In
this case, it is divided into two parts: an outside `wrapper' that uses
a `let' expression to determine the values of several variables that
need only be found once, such as the maximum height of the graph, and
an inside function that is called recursively to print the graph.
The `wrapper' is uncomplicated:
(defun recursive-graph-body-print (numbers-list)
"Print a bar graph of the NUMBERS-LIST.
The numbers-list consists of the Y-axis values."
(let ((height (apply 'max numbers-list))
(symbol-width (length graph-blank))
from-position)
(recursive-graph-body-print-internal
numbers-list
height
symbol-width)))
The recursive function is a little more difficult. It has four
parts: the `do-again-test', the printing code, the recursive call, and
the `next-step-expression'. The `do-again-test' is an `if' expression
that determines whether the `numbers-list' contains any remaining
elements; if it does, the function prints one column of the graph using
the printing code and calls itself again. The function calls itself
again according to the value produced by the `next-step-expression'
which causes the call to act on a shorter version of the `numbers-list'.
(defun recursive-graph-body-print-internal
(numbers-list height symbol-width)
"Print a bar graph.
Used within recursive-graph-body-print function."
(if numbers-list
(progn
(setq from-position (point))
(insert-rectangle
(column-of-graph height (car numbers-list)))
(goto-char from-position)
(forward-char symbol-width)
(sit-for 0) ; Draw graph column by column.
(recursive-graph-body-print-internal
(cdr numbers-list) height symbol-width))))
After installation, this expression can be tested; here is a sample:
(recursive-graph-body-print '(3 2 5 6 7 5 3 4 6 4 3 2 1))
Here is what `recursive-graph-body-print' produces:
*
** *
**** *
**** ***
* *********
************
*************
Either of these two functions, `graph-body-print' or
`recursive-graph-body-print', create the body of a graph.
File: emacs-lisp-intro.info, Node: Printed Axes, Next: Line Graph Exercise, Prev: recursive-graph-body-print, Up: Readying a Graph
Need for Printed Axes
=====================
A graph needs printed axes, so you can orient yourself. For a
do-once project, it may be reasonable to draw the axes by hand using
Emacs's Picture mode; but a graph drawing function may be used more
than once.
For this reason, I have written enhancements to the basic
`print-graph-body' function that automatically print labels for the
horizontal and vertical axes. Since the label printing functions do
not contain much new material, I have placed their description in an
appendix. *Note A Graph with Labelled Axes: Full Graph.
File: emacs-lisp-intro.info, Node: Line Graph Exercise, Prev: Printed Axes, Up: Readying a Graph
Exercise
========
Write a line graph version of the graph printing functions.
File: emacs-lisp-intro.info, Node: Emacs Initialization, Next: Debugging, Prev: Readying a Graph, Up: Top
Your `.emacs' File
******************
"You don't have to like Emacs to like it" - this seemingly
paradoxical statement is the secret of GNU Emacs. The plain, `out of
the box' Emacs is a generic tool. Most people who use it, customize it
to suit themselves.
GNU Emacs is mostly written in Emacs Lisp; this means that by writing
expressions in Emacs Lisp you can change or extend Emacs.
* Menu:
* Default Configuration:: Emacs has sensible defaults.
* Site-wide Init:: You can write site-wide init files.
* edit-options:: How to set some variables for a session.
* Beginning a .emacs File:: How to write a `.emacs file'.
* Text and Auto-fill:: Automatically wrap lines.
* Mail Aliases:: Use abbreviations for email addresses.
* Indent Tabs Mode:: Don't use tabs with TeX
* Keybindings:: Create some personal keybindings.
* Loading Files:: Load (i.e. evaluate) files automatically.
* Autoload:: Make functions available.
* Simple Extension:: Define a function; bind it to a key.
* Keymaps:: More about key binding.
* X11 Colors:: Colors in version 19 in X.
* V19 Miscellaneous:: Automatically resize minibuffer, and more.
* Mode Line:: How to customize your mode line.
File: emacs-lisp-intro.info, Node: Default Configuration, Next: Site-wide Init, Prev: Emacs Initialization, Up: Emacs Initialization
Emacs's Default Configuration
=============================
There are those who appreciate Emacs's default configuration. After
all, Emacs starts you in C mode when you edit a C file, starts you in
Fortran mode when you edit a Fortran file, and starts you in
Fundamental mode when you edit an unadorned file. This all makes
sense, if you do not know who is going to use Emacs. Who knows what a
person hopes to do with an unadorned file? Fundamental mode is the
right default for such a file, just as C mode is the right default for
editing C code. But when you do know who is going to use Emacs--you,
yourself--then it makes sense to customize Emacs.
For example, I seldom want Fundamental mode when I edit an otherwise
undistinguished file; I want Text mode. This is why I customize Emacs:
so it suits me.
You can customize and extend Emacs by writing or adapting a
`~/.emacs' file. This is your personal initialization file; its
contents, written in Emacs Lisp, tell Emacs what to do.
This chapter describes a simple `.emacs' file; for more information,
see *Note The Init File: (emacs)Init File, and *Note The Init File:
(elisp)Init File.
File: emacs-lisp-intro.info, Node: Site-wide Init, Next: edit-options, Prev: Default Configuration, Up: Emacs Initialization
Site-wide Initialization Files
==============================
In addition to your personal initialization file, Emacs automatically
loads various site-wide initialization files, if they exist. These
have the same form as your `.emacs' file, but are loaded by everyone.
Two site-wide initialization files, `site-load.el' and
`site-init.el', are loaded into Emacs and then `dumped' if a `dumped'
version of Emacs is created, as is most common. (Dumped copies of
Emacs load more quickly. However, once a file is loaded and dumped, a
change to it does not lead to a change in Emacs unless you load it
yourself or re-dump Emacs. *Note Building Emacs: (elisp)Building
Emacs, and the `INSTALL' file.)
Three other site-wide initialization files are loaded automatically
each time you start Emacs, if they exist. These are `site-start.el',
which is loaded *before* your `.emacs' file, and `default.el', and the
terminal type file, which are both loaded *after* your `.emacs' file.
Settings and definitions in your `.emacs' file will overwrite
conflicting settings and definitions in a `site-start.el' file, if it
exists; but the settings and definitions in a `default.el' or terminal
type file will overwrite those in your `.emacs' file. (You can prevent
interference from a terminal type file by setting `term-file-prefix' to
`nil'. *Note A Simple Extension: Simple Extension.)
The `INSTALL' file that comes in the distribution contains
descriptions of the `site-init.el' and `site-load.el' files.
The `loadup.el', `startup.el', and `loaddefs.el' files control
loading. These files are in the `lisp' directory of the Emacs
distribution and are worth perusing.
The `loaddefs.el' file contains a good many suggestions as to what
to put into your own `.emacs' file, or into a site-wide initialization
file.
File: emacs-lisp-intro.info, Node: edit-options, Next: Beginning a .emacs File, Prev: Site-wide Init, Up: Emacs Initialization
Setting Variables for One Session
=================================
My copy of Emacs version 19.23 has 392 options that you can set with
the `edit-options' command. These `options' are no more than variables
such as we have seen earlier and defined using `defvar'.
Emacs determines whether a variable is intended to be easily settable
by looking at the first character in its documentation string; if the
first character is an asterisk, `*', the variable is a user-settable
option. (*Note Initializing a Variable with `defvar': defvar.)
The `edit-options' command lists all the variables in Emacs that the
people who wrote the Emacs Lisp libraries think ought to be readily
settable. It provides an easy-to-use interface for resetting these
variables.
On the other hand, options set using `edit-options' are set only for
the duration of your editing session. The new values are not saved
between sessions. Each time Emacs starts, it reads the original
`defvar' value in its source code. To carry a changed setting from one
session to the next, you need to use a `setq' expression within a
`.emacs' file or other file that you load every time you start a
session.
For me, the major use of the `edit-options' command is to suggest
variables I might want to set in my `.emacs' file. I urge you to look
through the list.
*Note Editing Variable Values: (emacs)Edit Options, for more
information.
File: emacs-lisp-intro.info, Node: Beginning a .emacs File, Next: Text and Auto-fill, Prev: edit-options, Up: Emacs Initialization
Beginning a `.emacs' File
=========================
When you start Emacs, it loads your `.emacs' file unless you tell it
not to by specifying `-q' on the command line. (The `emacs -q' command
gives you a plain, out-of-the-box Emacs.)
A `.emacs' file contains Lisp expressions. Often, these are no more
than expressions to set values; sometimes they are function definitions.
*Note The Init File `~/.emacs': (emacs)Init File, for a short
description of initialization files.
This chapter goes over some of the same ground, but is a walk among
extracts from a complete, long-used `.emacs' file--my own.
The first part of the file consists of comments: reminders to myself.
By now, of course, I remember these things, but when I started, I did
not.
;;;; Bob's .emacs file
; Robert J. Chassell
; 26 September 1985
Look at that date! I started this file a long time ago. I have been
adding to it ever since.
; Each section in this file is introduced by a
; line beginning with four semicolons; and each
; entry is introduced by a line beginning with
; three semicolons.
This describes the usual conventions for comments in Emacs Lisp.
Everything on a line that follows a semicolon is a comment. Two,
three, and four semicolons are used as section and subsection markers.
(*Note Comments: (elisp)Comments, for more about comments.)
;;;; The Help Key
; Control-h is the help key;
; after typing control-h, type a letter to
; indicate the subject about which you want help.
; For an explanation of the help facility,
; type control-h three times in a row.
Just remember: type `C-h' three times for help.
; To find out about any mode, type control-h m
; while in that mode. For example, to find out
; about mail mode, enter mail mode and then type
; control-h m.
`Mode help', as I call this, is very helpful. Usually, it tells you
all you need to know.
Of course, you don't need to include comments like these in your
`.emacs' file. I included them in mine because I kept forgetting about
Mode help or the conventions for comments--but I was able to remember
to look here to remind myself.